package com.wthink.hzgj.data.repository

import com.wthink.hzgj.data.BusDataBase
import com.wthink.hzgj.data.BusRepository
import com.wthink.hzgj.data.Result
import com.wthink.hzgj.data.domain.Line
import com.wthink.hzgj.data.domain.Station
import com.wthink.hzgj.service.DevService
import com.wthink.hzgj.util.Log
import com.wthink.hzgj.util.Page
import kotlinx.coroutines.*

class DefaultBusRepository : BusRepository {

    companion object {
        private val lineDao by lazy {
            BusDataBase.getInstance().lineDao()
        }

        private val stationDao by lazy {
            BusDataBase.getInstance().stationDao()
        }

        @Volatile
        private var loading = false

        private suspend fun loadLineListFromUbusUtilSuccess(delay: Long): List<Line> {
            return withContext(Dispatchers.IO) {
                while (isActive) {
                    val remoteRet = DevService.getLineList()
                    if (remoteRet is Result.Success) {
                        return@withContext remoteRet.data
                    }

                    delay(delay)
                }
            } as List<Line>
        }

        private suspend fun loadLineDetailFromUbusUtilSuccess(
            line: Line,
            delay: Long
        ): List<Station> {
            return withContext(Dispatchers.IO) {
                while (isActive) {
                    val remoteRet = DevService.getLineDetail(line.line)
                    if (remoteRet is Result.Success) {
                        val lineDetail = remoteRet.data
                        line.name = lineDetail.lineName
                        val stationList =
                            ArrayList<Station>(lineDetail.up.size + lineDetail.down.size)
                        stationList.addAll(lineDetail.up)
                        stationList.addAll(lineDetail.down)

                        return@withContext stationList
                    }
                    delay(delay)
                }
            } as List<Station>
        }

        private suspend fun refreshInner(): Job? {
            if (loading) {
                return null
            }

            return GlobalScope.async(Dispatchers.IO) {
                loading = true
                val start = System.currentTimeMillis()
                try {
                    val lineList = loadLineListFromUbusUtilSuccess(2000)
                    val stationList = ArrayList<Station>()
                    lineList.forEach {
                        stationList.addAll(loadLineDetailFromUbusUtilSuccess(it, 2000))
                    }

                    stationDao.delete()
                    lineDao.delete()

                    lineDao.save(lineList)
                    stationDao.save(stationList)

                    Log.debug("从UBUS查询线路信息花费时间:${System.currentTimeMillis() - start}")
                } catch (err: Throwable) {
                    Log.debug("loading bus line err", err)
                } finally {
                    loading = false
                }
            }
        }
    }

    suspend fun getLineListFromLocal(): List<Line> {
        return lineDao.list()
    }


    override suspend fun refresh(): Job? {
        return refreshInner()
    }


    override suspend fun getStationList(line: String, updir: Boolean): List<Station> {
        return stationDao.listByLineDir(line, updir)
    }

    override suspend fun findStationById(stationId: String, lineId: String?): Station? {
        val list = stationDao.listById(stationId)
        if (list.isNotEmpty()) {
            return list[0]
        }

        return null
    }

    override suspend fun findLineById(lineId: String): Line? {
        return lineDao.findById(lineId)
    }

    override suspend fun findLineNameById(lineId: String): String? {
        return lineDao.findById(lineId)?.name
    }

    override suspend fun findLineIdByName(lineName: String): Long? {
        return lineDao.findById(lineName)?.id
    }

    override suspend fun findStationNameById(stationId: String): String? {
        val list = stationDao.listById(stationId)
        if (list.isEmpty()) {
            return null
        }

        return list.first().name
    }

    override suspend fun findStationIdByName(stationName: String): Long? {
        val list = stationDao.listByName(stationName)
        if (list.isEmpty()) {
            return null
        }

        return list.first().id
    }

    override suspend fun getLinePage(
        page: Int,
        onlyCache: Boolean,
        pageSize: Int
    ): Page<com.wthink.hzgj.home.data.Line> {
        return page(getLineListFromLocal().filter { it.name.isNotEmpty() }, page, pageSize)
    }

    override fun page(
        list: List<Line>,
        page: Int,
        pageSize: Int
    ): Page<com.wthink.hzgj.home.data.Line> {
        var pageTotal = list.size / pageSize + if (list.size % pageSize > 0) {
            1
        } else {
            0
        }

        if (pageTotal == 0) {
            pageTotal = 1
        }

        val end = page * pageSize + pageSize
        val subEnd = if (end >= list.size) {
            list.size
        } else {
            end
        }

        return Page(list.subList(page * pageSize, subEnd).map {
            com.wthink.hzgj.home.data.Line(
                it.line,
                it.name
            )
        }, page, pageTotal)
    }
}